﻿using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;

namespace CGF
{
    public class TextureFontCreatorOnFolder : EditorWindow
    {
        const int Border = 2;

        static string outputPath;
        static string originFolder;
        static string texSavePath, matSavePath, fontSavePath;
        static Texture2D atlasTex;

        class Char
        {
            public Texture2D tex;
            public string _char;
        }
        List<Char> chars = new List<Char>();
        bool isVisible;
        int space;


        [MenuItem("Assets/创建位图字体（散图）")]
        static void OpenWindow()
        {
            TextureFontCreatorOnFolder window = GetWindow<TextureFontCreatorOnFolder>();
            window.titleContent = new GUIContent("创建位图字体");
            window.isVisible = true;

            //获取目录内的素材
            window.chars = GetSelectedTextures();
        }

        /// <summary>
        /// 获取当前选择的图片
        /// </summary>
        static List<Char> GetSelectedTextures()
        {
            List<Char> chars = new List<Char>();
            DefaultAsset[] folders = Selection.GetFiltered<DefaultAsset>(SelectionMode.Assets);
            if (folders.Length == 0)
            {
                EditorUtility.DisplayDialog("提示", "请选择包含字符图片的文件目录", "确定");
                return chars;
            }
            if (folders.Length > 1)
            {
                EditorUtility.DisplayDialog("提示", "只能选择一个目录", "确定");
                return chars;
            }
            outputPath = AssetDatabase.GetAssetPath(folders[0]);
            Debug.Log($"outputPath: {outputPath}");

            List<string> allTexPaths = new List<string>();
            string folder = AssetDatabase.GetAssetPath(folders[0]);
            string[] pngPaths = Directory.GetFiles(folder, "*.png", SearchOption.TopDirectoryOnly);
            for (int j = 0; j < pngPaths.Length; j++)
            {
                string texPath = pngPaths[j].Replace("\\", "/");
                if (!allTexPaths.Contains(texPath)) allTexPaths.Add(texPath);
            }
            for (int i = 0; i < allTexPaths.Count; i++)
            {
                string texPath = allTexPaths[i];
                int index = texPath.LastIndexOf("/");
                //设置为可读写，取消压缩，以便正确读取图片像素信息
                TextureImporter importer = AssetImporter.GetAtPath(texPath) as TextureImporter;
                if (importer)
                {
                    importer.npotScale = TextureImporterNPOTScale.None;
                    importer.isReadable = true;
                    importer.mipmapEnabled = false;
                    importer.wrapMode = TextureWrapMode.Clamp;
                    importer.textureCompression = TextureImporterCompression.Uncompressed;

                    TextureImporterPlatformSettings androidSetting = importer.GetPlatformTextureSettings("Android");
                    androidSetting.overridden = false;
                    importer.SetPlatformTextureSettings(androidSetting);
                    TextureImporterPlatformSettings iosSetting = importer.GetPlatformTextureSettings("iPhone");
                    iosSetting.overridden = false;
                    importer.SetPlatformTextureSettings(iosSetting);

                    AssetDatabase.ImportAsset(texPath);
                }

                //这里不用AssetDatabase.LoadAssetAtPath(),否则读不到小数点命名的图片
                Texture2D texture = new Texture2D(0, 0, TextureFormat.ARGB32, false);
                texture.LoadImage(File.ReadAllBytes(texPath));
                string texName = System.IO.Path.GetFileNameWithoutExtension(texPath).Trim();
                Char c = new Char();
                c.tex = texture;
                c._char = texName.Length == 1 ? texName : "";
                chars.Add(c);
            }
            if (allTexPaths.Count > 0)
            {
                string[] arr = allTexPaths[0].Split('/');
                originFolder = arr[arr.Length - 2];
                Debug.Log($"originFolder: {originFolder}");
            }

            Selection.objects = null;

            return chars;
        }

        void OnGUI()
        {
            EditorGUILayout.Space(10);
            for (int i = 0; i < chars.Count; i++)
            {
                EditorGUILayout.BeginHorizontal();
                chars[i].tex = (Texture2D)EditorGUILayout.ObjectField(chars[i].tex, typeof(Texture2D), true);
                EditorGUILayout.LabelField(" ➜", GUILayout.MaxWidth(30));
                chars[i]._char = EditorGUILayout.TextField(chars[i]._char);
                if (GUILayout.Button("X", GUILayout.MaxWidth(20)))
                {
                    chars.RemoveAt(i);
                }
                EditorGUILayout.EndHorizontal();
            }
            if (GUILayout.Button("✚"))
            {
                chars.Add(new Char());
            }
            EditorGUILayout.Space(10);
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("字符间距", GUILayout.MaxWidth(60));
            space = EditorGUILayout.IntField(space);
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.Space(10);
            if (GUILayout.Button("生成 Bitmap Font") || Event.current.keyCode == KeyCode.Return || Event.current.keyCode == KeyCode.KeypadEnter)
            {
                if (isVisible)
                {
                    for (int i = 0; i < chars.Count; i++)
                    {
                        if (string.IsNullOrEmpty(chars[i]._char.Trim()))
                        {
                            EditorUtility.DisplayDialog("提示", "字符映射不能为空", "确定");
                            return;
                        }
                    }
                    Close();
                    StartCreateFont(space);
                    isVisible = false;
                }
            }
        }

        void StartCreateFont(int space)
        {
            //将素材打包成图集
            Dictionary<string, Rect> sprites = TexturesToAtlas(chars);

            //根据图集生成字体
            bool success = CreateFontFiles(sprites, space);
            if (success)
            {
                Object obj = AssetDatabase.LoadMainAssetAtPath(texSavePath);
                //定位到导出文件
                EditorGUIUtility.PingObject(obj);
                Selection.activeObject = obj;
            }
        }

        /// <summary>
        /// 将多张小图拼接成图集并保存,返回每个小图的位置坐标
        /// </summary>
        Dictionary<string, Rect> TexturesToAtlas(List<Char> chars)
        {
            Dictionary<string, Rect> charRects = new Dictionary<string, Rect>();
            List<string> keys = new List<string>();
            for (int i = 0; i < chars.Count; i++)
            {
                keys.Add(chars[i]._char);
            }
            List<Texture2D> texList = new List<Texture2D>();
            for (int i = 0; i < chars.Count; i++)
            {
                texList.Add(chars[i].tex);
            }

            atlasTex = new Texture2D(0, 0, TextureFormat.RGBA32, false);
            Rect[] rects = atlasTex.PackTextures(texList.ToArray(), Border, 1024, false);
            for (int i = 0; i < rects.Length; i++)
            {
                Rect rect = rects[i];
                rect.x *= atlasTex.width;
                rect.y *= atlasTex.height;
                rect.width *= atlasTex.width;
                rect.height *= atlasTex.height;
                charRects.Add(keys[i], rect);
            }

            string texName = originFolder + ".png";
            string matName = originFolder + ".mat";
            string fontName = originFolder + ".fontsettings";

            string saveFolderPath = Application.dataPath.Replace("Assets", "") + outputPath + "/" + originFolder;
            if (!Directory.Exists(saveFolderPath)) Directory.CreateDirectory(saveFolderPath);
            File.WriteAllBytes(saveFolderPath + "/" + texName, atlasTex.EncodeToPNG());

            texSavePath = outputPath + "/" + originFolder + "/" + texName;
            matSavePath = outputPath + "/" + originFolder + "/" + matName;
            fontSavePath = outputPath + "/" + originFolder + "/" + fontName;

            AssetDatabase.ImportAsset(texSavePath);
            TextureImporter importer = AssetImporter.GetAtPath(texSavePath) as TextureImporter;
            if (importer)
            {
                importer.npotScale = TextureImporterNPOTScale.None;
                importer.mipmapEnabled = false;
                importer.textureCompression = TextureImporterCompression.Uncompressed;
                AssetDatabase.ImportAsset(texSavePath);
            }

            return charRects;
        }

        /// <summary>
        /// 创建字体文件和材质球
        /// </summary>
        bool CreateFontFiles(Dictionary<string, Rect> sprites, int space)
        {
            Material mat;
            if (File.Exists(matSavePath))
            {
                mat = AssetDatabase.LoadAssetAtPath<Material>(matSavePath);
            }
            else
            {
                mat = new Material(Shader.Find("GUI/Text Shader"));
                AssetDatabase.CreateAsset(mat, matSavePath);
            }
            mat.mainTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(texSavePath);
            Font font;
            if (File.Exists(fontSavePath))
            {
                font = AssetDatabase.LoadAssetAtPath<Font>(fontSavePath);
            }
            else
            {
                font = new Font();
                AssetDatabase.CreateAsset(font, fontSavePath);
            }
            font.material = mat;
            CharacterInfo[] characterInfo = new CharacterInfo[sprites.Count + 1];
            int i = 0;
            int charW = 0;
            int charH = 0;
            foreach (var item in sprites)
            {
                Rect rect = item.Value;
                charW = (int)rect.width;
                charH = (int)rect.height;
                CharacterInfo info = new CharacterInfo();
                info.index = item.Key.ToCharArray()[0];
                //设置字符映射到材质上的坐标
                info.uvBottomLeft = new Vector2(rect.x / (float)atlasTex.width, rect.y / (float)atlasTex.height);
                info.uvBottomRight = new Vector2((rect.x + rect.width) / (float)atlasTex.width, rect.y / (float)atlasTex.height);
                info.uvTopLeft = new Vector2(rect.x / (float)atlasTex.width, (rect.y + rect.height) / (float)atlasTex.height);
                info.uvTopRight = new Vector2((rect.x + rect.width) / (float)atlasTex.width, (rect.y + rect.height) / (float)atlasTex.height);
                //字符顶点的偏移位置和宽高
                info.minX = Mathf.RoundToInt(space * 0.5f);
                info.maxX = (int)rect.width + info.minX;
                info.minY = -(int)(rect.height * 0.5f);
                info.maxY = (int)(rect.height * 0.5f);
                //字符间距  
                info.advance = (int)rect.width + space;
                characterInfo[i] = info;
                i++;
            }
            //支持空格
            CharacterInfo spaceInfo = new CharacterInfo();
            spaceInfo.index = (" ")[0];
            spaceInfo.advance = Mathf.RoundToInt(charW * 0.8f) + space;
            characterInfo[sprites.Count] = spaceInfo;

            font.characterInfo = characterInfo;
            EditorUtility.SetDirty(font);

            AssetDatabase.SaveAssets();

            //某些属性无法设置，直接暴力修改文件内容
            SerializedObject serialized = new SerializedObject(AssetDatabase.LoadAssetAtPath<Object>(fontSavePath));
            serialized.Update();
            serialized.FindProperty("m_LineSpacing").floatValue = charH;
            serialized.FindProperty("m_FontSize").floatValue = 38f;
            serialized.ApplyModifiedProperties();
            serialized.SetIsDifferentCacheDirty();
            AssetDatabase.ImportAsset(fontSavePath);

            return true;
        }
    }
}